package com.opencee.common.security.oauth2;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 自定义GrantType认证
 *
 * @author yadu
 */
public class CustomTokenGranter extends AbstractTokenGranter implements ApplicationContextAware {

    private List<CustomTokenAuthenticationHandler> handlers;
    private AuthenticationEventPublisher eventPublisher;

    public CustomTokenGranter(AuthorizationServerEndpointsConfigurer endpointsConfigurer, String grantType, List<CustomTokenAuthenticationHandler> handlers) {
        super(endpointsConfigurer.getTokenServices(), endpointsConfigurer.getClientDetailsService(), endpointsConfigurer.getOAuth2RequestFactory(), grantType);
        this.handlers = handlers;
    }


    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
        Authentication userAuth = null;
        // 前置验证
        try {
            this.prepare(tokenRequest);
            // 认证
            UserDetails principal = this.authenticate(tokenRequest);
            if (principal != null) {
                userAuth = new UsernamePasswordAuthenticationToken(principal, "N/A", principal.getAuthorities());
            }
            if (userAuth == null || !userAuth.isAuthenticated()) {
                throw new InvalidGrantException("Could not authenticate user");
            }
            OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
            OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(storedOAuth2Request, userAuth);
            // 认证成功调用
            this.eventPublisher.publishAuthenticationSuccess(oAuth2Authentication);
            return oAuth2Authentication;
        } catch (Exception  e) {
            this.eventPublisher.publishAuthenticationFailure(new InsufficientAuthenticationException(e.getMessage()), userAuth);
        }
        return null;
    }

    /**
     * 静态方法获取HttpServletRequest对象
     *
     * @return
     */
    public static HttpServletRequest getHttpServletRequest() {
        try {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 静态方法HttpServletResponse对象
     *
     * @return
     */
    public static HttpServletResponse getHttpServletResponse() {
        try {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 认证处理
     *
     * @param tokenRequest
     * @return
     */
    private UserDetails authenticate(TokenRequest tokenRequest) throws AuthenticationException {
        if (this.handlers != null) {
            for (CustomTokenAuthenticationHandler exchanger : handlers) {
                if (tokenRequest.getGrantType().equals(exchanger.grantType())) {
                    return exchanger.authenticate(tokenRequest);
                }
            }
        }
        return null;
    }

    /**
     * 前置认证处理
     */
    private void prepare(TokenRequest tokenRequest) throws Exception {
        if (this.handlers != null) {
            for (CustomTokenAuthenticationHandler exchanger : handlers) {
                if (tokenRequest.getGrantType().equals(exchanger.grantType())) {
                    exchanger.prepare(tokenRequest);
                    return;
                }
            }
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.eventPublisher = applicationContext.getBean(AuthenticationEventPublisher.class);
    }
}
